package com.uinnova.product.eam.service.bm.impl;

import com.binary.core.util.BinaryUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.model.bm.FlowModelMergeParams;
import com.uinnova.product.eam.service.EamCategorySvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.dix.api.AppSystemModelSvc;
import com.uinnova.product.eam.service.dix.api.AppSystemSyncRecordSvc;
import com.uinnova.product.eam.service.handler.PushFacadeService;
import com.uinnova.product.eam.service.impl.IamsCIRltDesignSvc;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESAttrBean;
import com.uino.bean.cmdb.query.ESCISearchBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 视图发布/检出后置处理
 * @author ch
 */
@Slf4j
@Component
@EnableAsync
public class FlowModelMergePostProcessor {

    private static final String DM_REFERENCE = "DM-引用";
    private static final String BELONG = "归属";

    private static final String BELONG_SCHEME = "所属主题域";
    private static final String NAME = "名称";
    /**
     * 企业级主题域分类标识
     */
    private static final String DM_SUB_DOMAIN = "DM_SUB_DOMAIN";
    /**
     * 业务对象分类标识
     */
    private static final String DM_BUS_OBJ = "DM_BUS_OBJ";
    @Resource
    private EamCategorySvc categorySvc;
    @Resource
    private ICISwitchSvc iciSwitchSvc;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private IRltClassApiSvc rltClassApiSvc;
    @Resource
    private IamsCIRltDesignSvc rltDesignSvc;
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Resource
    private ESDiagramDao esDiagramDao;
    @Resource
    private PushFacadeService pushFacadeService;
    @Resource
    private AppSystemSyncRecordSvc appSystemSyncRecordSvc;
    @Autowired
    private AppSystemModelSvc appSystemModelSvc;

    public void execute(FlowModelMergeParams params){
        log.info("====================发布逻辑后置处理开始=======================");
        pushFacadeService.bindVersionNoByDiagramId(params.getDiagramIds());
        bindBusinessAndDomainRlt(params);
        handleCiRlt(params);
        //应用子系统实体数据推送
        appSystemModelSvc.syncAppSystemDataModel(params.getMergeCiList());
        log.info("====================发布逻辑后置处理结束=======================");
    }

    /**
     * 绑定主题域和业务对象之间的关系
     * 业务规则：当前目录绑定的对象若是主题域，则查询其目录下所有视图中的业务对象，创建"引用"关系
     * @param params 参数
     */
    @Async
    public void bindBusinessAndDomainRlt(FlowModelMergeParams params){
        List<EamCategory> designCategorys = params.getDesignModelList();
        if(BinaryUtils.isEmpty(designCategorys)){
            return;
        }
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(params.getDomainId(), DM_REFERENCE);
        if(BinaryUtils.isEmpty(rltClass)){
            log.error(DM_REFERENCE+"关系分类未创建!");
            return;
        }
        Long rltId = rltClass.getCiClass().getId();
        CcCiClassInfo domainClass = ciClassApiSvc.getCIClassByCodes(DM_SUB_DOMAIN);
        CcCiClassInfo busClass = ciClassApiSvc.getCIClassByCodes(DM_BUS_OBJ);
        if(BinaryUtils.isEmpty(domainClass.getCiClass()) || BinaryUtils.isEmpty(busClass.getCiClass())){
            log.error("业务对象分类或主题域对象分类未创建!");
            return;
        }
        List<String> ciCodes = designCategorys.stream().map(EamCategory::getCiCode).distinct().collect(Collectors.toList());
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(ciCodes, null, LibType.DESIGN);
        if(BinaryUtils.isEmpty(ciList)){
            return;
        }
        Long domainClassId = domainClass.getCiClass().getId();
        List<ESCIInfo> domainCiList = ciList.stream().filter(each -> domainClassId.equals(each.getClassId())).collect(Collectors.toList());
        if(BinaryUtils.isEmpty(domainCiList)){
            return;
        }
        Map<String, ESCIInfo> domainCiMap = domainCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        Set<String> domainCiCodes = domainCiMap.keySet();
        List<EamCategory> domainCategoryList = designCategorys.stream().filter(each -> domainCiCodes.contains(each.getCiCode())).collect(Collectors.toList());
        if(BinaryUtils.isEmpty(domainCategoryList)){
            return;
        }
        Map<Long, EamCategory> domainDirMap = domainCategoryList.stream().collect(Collectors.toMap(EamCategory::getId, each -> each, (k1, k2) -> k2));
        List<ESDiagram> diagramList = diagramApiClient.selectByDirIds(domainDirMap.keySet(), Lists.newArrayList(101), null, Lists.newArrayList(1));
        if(BinaryUtils.isEmpty(diagramList)){
            return;
        }
        List<Long> diagramIds = diagramList.stream().map(ESDiagram::getId).distinct().collect(Collectors.toList());
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramIds);
        if(BinaryUtils.isEmpty(nodeList)){
            return;
        }
        Set<String> nodeCiCodeList = new HashSet<>();
        Map<String, Set<String>> nodeGroup = new HashMap<>(nodeList.size());
        for (ESDiagramNode node : nodeList) {
            if(BinaryUtils.isEmpty(node.getCiCode())){
                continue;
            }
            nodeCiCodeList.add(node.getCiCode());
            nodeGroup.putIfAbsent(node.getdEnergy(), new HashSet<>());
            nodeGroup.get(node.getdEnergy()).add(node.getCiCode());
        }
        if(BinaryUtils.isEmpty(nodeCiCodeList)){
            return;
        }
        List<ESCIInfo> nodeCiList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(nodeCiCodeList), null, LibType.DESIGN);
        Long busClassId = busClass.getCiClass().getId();
        List<ESCIInfo> busCiList = nodeCiList.stream().filter(each -> busClassId.equals(each.getClassId())).collect(Collectors.toList());
        if(BinaryUtils.isEmpty(busCiList)){
            return;
        }
        Map<String, ESCIInfo> busCiMap = busCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));

        Map<Long, Set<String>> diagramDirGroup = diagramList.stream().collect(Collectors.groupingBy(ESDiagram::getDirId, Collectors.mapping(ESDiagram::getDEnergy,Collectors.toSet())));
        List<ESCIRltInfo> bindList = new ArrayList<>();
        for (EamCategory category : domainCategoryList) {
            Set<String> currDiagramIds = diagramDirGroup.get(category.getId());
            if(BinaryUtils.isEmpty(currDiagramIds)){
                continue;
            }
            List<String> allCiCodes = new ArrayList<>();
            for (String diagramId : currDiagramIds) {
                Set<String> nodeCodes = nodeGroup.get(diagramId);
                if(BinaryUtils.isEmpty(nodeCodes)){
                    continue;
                }
                allCiCodes.addAll(nodeCodes);
            }
            ESCIInfo domainCi = domainCiMap.get(category.getCiCode());
            if(BinaryUtils.isEmpty(domainCi)){
                continue;
            }
            String domainCode = domainCi.getCiCode();
            for (String code : allCiCodes) {
                ESCIInfo busCi = busCiMap.get(code);
                if(BinaryUtils.isEmpty(busCi)){
                    continue;
                }
                ESCIRltInfo rlt = new ESCIRltInfo();
                rlt.setSourceClassId(domainClassId);
                rlt.setTargetClassId(busClassId);
                rlt.setClassId(rltId);
                rlt.setSourceCiCode(domainCode);
                rlt.setTargetCiCode(busCi.getCiCode());
                rlt.setCiCode(domainCode+"_"+rltId+"_"+busCi.getCiCode());
                rlt.setUniqueCode("UK_"+rlt.getCiCode());
                rlt.setAttrs(Maps.newHashMap());
                bindList.add(rlt);
            }
        }
        rltDesignSvc.bindBatchCiRlt(bindList, params.getOwnerCode());
    }

    /**
     * 处理业务对象和主题域之间的绑定关系--归属
     * @author wcl
     * @param params 参数
     */
    @Async
    public void handleCiRlt(FlowModelMergeParams params) {
        List<ESCIInfo> privateCiList = params.getMergeCiList();
        if(BinaryUtils.isEmpty(privateCiList)){
            return;
        }
        List<String> ciCodes = privateCiList.stream().map(CcCi::getCiCode).distinct().collect(Collectors.toList());
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(ciCodes, null, LibType.DESIGN);
        if(BinaryUtils.isEmpty(ciList)){
            return;
        }
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(params.getDomainId(), BELONG);
        if (BinaryUtils.isEmpty(rltClass)) {
            log.error(BELONG + "关系分类未创建!");
            return;
        }
        Long rltId = rltClass.getCiClass().getId();
        //企业级主题域
        CcCiClassInfo domainClass = ciClassApiSvc.getCIClassByCodes(DM_SUB_DOMAIN);
        //业务对象
        CcCiClassInfo busClass = ciClassApiSvc.getCIClassByCodes(DM_BUS_OBJ);
        if (BinaryUtils.isEmpty(domainClass.getCiClass()) || BinaryUtils.isEmpty(busClass.getCiClass())) {
            log.error("业务对象分类或主题域对象分类未创建!");
            return;
        }
        Long schemeFileId = domainClass.getCiClass().getId();
        Long businessId = busClass.getCiClass().getId();
        List<String> ciNames = new ArrayList<>();
        //key=业务对象ciCode,value=绑定主题域的ciName
        Map<String, String> businessMap = new HashMap<>(ciList.size());
        for (ESCIInfo ci : ciList) {
            if(!ci.getClassId().equals(businessId)){
                continue;
            }
            //过滤出绑定的主题域不为空的业务对象
            if (!BinaryUtils.isEmpty(ci.getAttrs().get(BELONG_SCHEME))) {
                String name = ci.getAttrs().get(BELONG_SCHEME).toString();
                ciNames.add(name);
                businessMap.put(ci.getCiCode(), name);
            }
        }
        if (BinaryUtils.isEmpty(ciNames)) {
            log.info("视图中无绑定主题域的业务对象");
            return;
        }
        //取到每一个ci数据以及绑定的所属主题域的ciCode,关系创建到设计库；
        ESCISearchBean bean = new ESCISearchBean();
        bean.setPageNum(1);
        bean.setPageSize(10000);
        bean.setClassIds(Collections.singletonList(schemeFileId));
        List<ESAttrBean> attrBeans= new ArrayList<>();
        ciNames.forEach(e -> {
            ESAttrBean attrBean = new ESAttrBean();
            attrBean.setKey("名称");
            attrBean.setValue(e);
            attrBean.setOptType(1);
            attrBeans.add(attrBean);
        });
        bean.setOrAttrs(attrBeans);
        List<ESCIInfo> data = iciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN).getData();
        if (BinaryUtils.isEmpty(data)) {
            log.info("未查询到绑定的主题域对象");
            return;
        }
        //主题域的ci信息 key=主题域ciName,value=主题域ciCode
        Map<String, String> schemeCiMap = new HashMap<>(data.size());
        for (ESCIInfo datum : data) {
            if(BinaryUtils.isEmpty(datum.getAttrs().get(NAME)) || BinaryUtils.isEmpty(datum.getCiCode())){
                continue;
            }
            schemeCiMap.put(datum.getAttrs().get(NAME).toString(), datum.getCiCode());
        }
        //创建业务对象和主题域之间的归属关系
        List<ESCIRltInfo> bindList = new ArrayList<>();
        //遍历业务对象的map
        for (String key : businessMap.keySet()) {
            String name = businessMap.get(key);
            String schemeCode = schemeCiMap.get(name);
            ESCIRltInfo rlt = new ESCIRltInfo();
            rlt.setSourceClassId(businessId);
            rlt.setTargetClassId(schemeFileId);
            rlt.setClassId(rltId);
            rlt.setSourceCiCode(key);
            rlt.setTargetCiCode(schemeCode);
            rlt.setCiCode(key + "_" + rltId + "_" + schemeCode);
            rlt.setUniqueCode("UK_" + rlt.getCiCode());
            rlt.setAttrs(Maps.newHashMap());
            bindList.add(rlt);
        }
        rltDesignSvc.bindBatchCiRlt(bindList, params.getOwnerCode());
    }

    /**
     * 发布/检出操作后,删除没有关联上级视图且没有绑定默认视图的目录
     * @param params 参数
     */
    public void refreshCategory(FlowModelMergeParams params, LibType libType) {
        List<EamCategory> categoryList = LibType.PRIVATE.equals(libType)?params.getPrivateModelList():params.getDesignModelList();
        if(BinaryUtils.isEmpty(categoryList)){
            return;
        }
        // 这部分前面处理过了 先改造好 在注释掉 出问题在考虑这个
        // refreshCategoryDirPath(params.getModelId(), params.getOwnerCode(), libType);
        List<String> energyIds = new ArrayList<>();
        Map<Long, EamCategory> dirIdMap = new HashMap<>(categoryList.size());
        for (EamCategory each : categoryList) {
            if(!BinaryUtils.isEmpty(each.getDiagramId())){
                energyIds.add(each.getDiagramId());
            }
            dirIdMap.put(each.getId(), each);
        }
        if(BinaryUtils.isEmpty(energyIds)){
            return;
        }
        List<ESDiagram> esDiagrams = esDiagramDao.getListByQuery(QueryBuilders.termsQuery("dEnergy.keyword", energyIds));
        if (BinaryUtils.isEmpty(esDiagrams)) {
            return;
        }
        List<Long> diagramIds = esDiagrams.stream().map(ESDiagram::getId).collect(Collectors.toList());
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(Lists.newArrayList(diagramIds));
        if(BinaryUtils.isEmpty(nodeList)){
            return;
        }
        // 视图 与 视图内的ciCode map
        Map<String, List<ESDiagramNode>> nodeGroup = nodeList.stream().collect(Collectors.groupingBy(ESDiagramNode::getdEnergy));
        // 父级 与 父级下的视图的ID map
        Map<Long, List<EamCategory>> dirGroup = categoryList.stream().collect(Collectors.groupingBy(EamCategory::getParentId));
        List<Long> filterIds = new ArrayList<>();
        for (Long preId : dirGroup.keySet()) {
            if(preId.equals(0L)){
                continue;
            }
            // 父级目录信息
            EamCategory preCategory = dirIdMap.get(preId);
            List<ESDiagramNode> currNode = new ArrayList<>();
            if(!BinaryUtils.isEmpty(preCategory) && !BinaryUtils.isEmpty(preCategory.getDiagramId())){
                currNode = nodeGroup.getOrDefault(preCategory.getDiagramId(), new ArrayList<>());
            }
            // 父级视图内的CI
            List<String> ciList = currNode.stream().map(ESDiagramNode::getCiCode).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            List<EamCategory> childList = dirGroup.get(preId);
            // 这里排除发布目录的 上级到L0层级的目录
            List<Long> expDirIds = params.getExpDirIds();
            List<Long> initDesignCategroyIds = params.getInitDesignCategroyIds();
            for (EamCategory each : childList) {
                if (libType.equals(LibType.DESIGN)) {
                    if(BinaryUtils.isEmpty(each.getDiagramId()) && !ciList.contains(each.getCiCode()) &&
                            each.getType() == 5 && !expDirIds.contains(each.getId()) &&
                            !initDesignCategroyIds.contains(each.getId())){
                        filterIds.add(each.getId());
                    }
                } else {
                    if(BinaryUtils.isEmpty(each.getDiagramId()) && !ciList.contains(each.getCiCode()) && each.getType() == 5){
                        filterIds.add(each.getId());
                    }
                }

            }
        }

        if(BinaryUtils.isEmpty(filterIds)){
            return;
        }
        List<ESDiagram> diagramList = diagramApiClient.selectByDirIds(filterIds, null, null, null);
        List<Long> delIds = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(diagramList)){
            Map<Long, List<ESDiagram>> diagramGroup = diagramList.stream().collect(Collectors.groupingBy(ESDiagram::getDirId));
            for (Long delId : filterIds) {
                if(CollectionUtils.isEmpty(diagramGroup.get(delId))){
                    delIds.add(delId);
                }
            }
        }else{
            delIds = filterIds;
        }
        categorySvc.delByIds(delIds, libType);
    }

    private void refreshCategoryDirPath(Long modelId, String ownerCode, LibType libType){
        List<EamCategory> categoryList = categorySvc.selectByModelId(modelId, libType, ownerCode);
        EamCategory modelRoot = categorySvc.getModelRoot(modelId, ownerCode, libType);
        Integer modelStartLvl = modelRoot.getDirLvl();
        categoryList.sort(Comparator.comparing(EamCategory::getDirLvl));
        Map<Long, EamCategory> categoryMap = new HashMap<>();
        for (EamCategory each : categoryList) {
            if((each.getDirLvl() - modelStartLvl) ==0){
                EamCategory parentCategoryInfo = categorySvc.getById(each.getParentId(), libType);
                each.setDirPath(parentCategoryInfo.getDirPath() + each.getId() + "#");
            }else{
                EamCategory parent = categoryMap.get(each.getParentId());
                if(BinaryUtils.isEmpty(parent)){
                    continue;
                }
                each.setDirPath(parent.getDirPath() + each.getId() + "#");
            }
            categoryMap.put(each.getId(), each);
        }
        categorySvc.saveOrUpdateList(categoryList, libType);
    }
}
